#ifndef __EXT2_H__
#define __EXT2_H__

#include <cnix/types.h>
#include <cnix/fs.h>
#include <cnix/kernel.h>
#include <cnix/mm.h>
#include <cnix/rbtree.h>
#include <asm/bitops.h>

/* data type for filesystem-wide blocks number */
typedef unsigned long ext2_fsblk_t;

/*
 * Structure of the super block
 */
struct ext2_super_block {
  u32_t  s_inodes_count;   /* Inodes count */
  u32_t  s_blocks_count;   /* Blocks count */
  u32_t  s_r_blocks_count; /* Reserved blocks count */
  u32_t  s_free_blocks_count;  /* Free blocks count */
  u32_t  s_free_inodes_count;  /* Free inodes count */
  u32_t  s_first_data_block; /* First Data Block */
  u32_t  s_log_block_size; /* Block size */
  u32_t  s_log_frag_size;  /* Fragment size */
  u32_t  s_blocks_per_group; /* # Blocks per group */
  u32_t  s_frags_per_group;  /* # Fragments per group */
  u32_t  s_inodes_per_group; /* # Inodes per group */
  u32_t  s_mtime;    /* Mount time */
  u32_t  s_wtime;    /* Write time */
  u16_t  s_mnt_count;    /* Mount count */
  u16_t  s_max_mnt_count;  /* Maximal mount count */
  u16_t  s_magic;    /* Magic signature */
  u16_t  s_state;    /* File system state */
  u16_t  s_errors;   /* Behaviour when detecting errors */
  u16_t  s_minor_rev_level;  /* minor revision level */
  u32_t  s_lastcheck;    /* time of last check */
  u32_t  s_checkinterval;  /* max. time between checks */
  u32_t  s_creator_os;   /* OS */
  u32_t  s_rev_level;    /* Revision level */
  u16_t  s_def_resuid;   /* Default uid for reserved blocks */
  u16_t  s_def_resgid;   /* Default gid for reserved blocks */
  /*
   * These fields are for EXT2_DYNAMIC_REV superblocks only.
   *
   * Note: the difference between the compatible feature set and
   * the incompatible feature set is that if there is a bit set
   * in the incompatible feature set that the kernel doesn't
   * know about, it should refuse to mount the filesystem.
   * 
   * e2fsck's requirements are more strict; if it doesn't know
   * about a feature in either the compatible or incompatible
   * feature set, it must abort and not try to meddle with
   * things it doesn't understand...
   */
  u32_t  s_first_ino;    /* First non-reserved inode */
  u16_t   s_inode_size;    /* size of inode structure */
  u16_t  s_block_group_nr;   /* block group # of this superblock */
  u32_t  s_feature_compat;   /* compatible feature set */
  u32_t  s_feature_incompat;   /* incompatible feature set */
  u32_t  s_feature_ro_compat;  /* readonly-compatible feature set */
  u8_t  s_uuid[16];   /* 128-bit uuid for volume */
  char  s_volume_name[16];  /* volume name */
  char  s_last_mounted[64];   /* directory where last mounted */
  u32_t  s_algorithm_usage_bitmap; /* For compression */
  /*
   * Performance hints.  Directory preallocation should only
   * happen if the EXT2_COMPAT_PREALLOC flag is on.
   */
  u8_t  s_prealloc_blocks;  /* Nr of blocks to try to preallocate*/
  u8_t  s_prealloc_dir_blocks;  /* Nr to preallocate for dirs */
  u16_t s_padding1;
  /*
   * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set.
   */
  u8_t  s_journal_uuid[16]; /* uuid of journal superblock */
  u32_t s_journal_inum;   /* inode number of journal file */
  u32_t s_journal_dev;    /* device number of journal file */
  u32_t s_last_orphan;    /* start of list of inodes to delete */
  u32_t s_hash_seed[4];   /* HTREE hash seed */
  u8_t  s_def_hash_version; /* Default hash version to use */
  u8_t  s_reserved_char_pad;
  u16_t s_reserved_word_pad;
  u32_t  s_default_mount_opts;
  u32_t  s_first_meta_bg;  /* First metablock block group */
  u32_t s_reserved[190];  /* Padding to the end of the block */
};

/*
 * Behaviour when detecting errors
 */
#define EXT2_ERRORS_CONTINUE    1 /* Continue execution */
#define EXT2_ERRORS_RO      2 /* Remount fs read-only */
#define EXT2_ERRORS_PANIC   3 /* Panic */
#define EXT2_ERRORS_DEFAULT   EXT2_ERRORS_CONTINUE

#define MAX_LFS_FILESIZE  (((u64_t)4096 << (BITS_PER_LONG-1))-1) 

/*
 * Constants relative to the data blocks
 */
#define EXT2_NDIR_BLOCKS    12
#define EXT2_IND_BLOCK      EXT2_NDIR_BLOCKS
#define EXT2_DIND_BLOCK     (EXT2_IND_BLOCK + 1)
#define EXT2_TIND_BLOCK     (EXT2_DIND_BLOCK + 1)
#define EXT2_N_BLOCKS     (EXT2_TIND_BLOCK + 1)

struct ext2_reserve_window {
  ext2_fsblk_t    _rsv_start; /* First byte reserved */
  ext2_fsblk_t    _rsv_end; /* Last byte reserved or 0 */
};

struct ext2_reserve_window_node {
  struct rb_node    rsv_node;
  u32_t     rsv_goal_size;
  u32_t     rsv_alloc_hit;
  struct ext2_reserve_window  rsv_window;
};

#define rsv_start rsv_window._rsv_start
#define rsv_end rsv_window._rsv_end

/*
 * second extended-fs super-block data in memory
 */
struct ext2_sb_info {
  unsigned long s_frag_size;  /* Size of a fragment in bytes */
  unsigned long s_frags_per_block;/* Number of fragments per block */
  unsigned long s_inodes_per_block;/* Number of inodes per block */
  unsigned long s_frags_per_group;/* Number of fragments in a group */
  unsigned long s_blocks_per_group;/* Number of blocks in a group */
  unsigned long s_inodes_per_group;/* Number of inodes in a group */
  unsigned long s_itb_per_group;  /* Number of inode table blocks per group */
  unsigned long s_gdb_count;  /* Number of group descriptor blocks */
  unsigned long s_desc_per_block; /* Number of group descriptors per block */
  unsigned long s_groups_count; /* Number of groups in the fs */
  unsigned long s_overhead_last;  /* Last calculated overhead */
  unsigned long s_blocks_last;    /* Last seen block count */
  struct buf_head * s_sbh_cnix; /* Buffer containing the super block */
  struct ext2_super_block * s_es; /* Pointer to the super block in the buffer */
  u8_t ** s_group_desc;
  //struct buf_head ** s_group_desc;
  unsigned long  s_mount_opt;
  unsigned long s_sb_block;
  uid_t s_resuid;
  gid_t s_resgid;
  unsigned short s_mount_state;
  unsigned short s_pad;
  int s_addr_per_block_bits;
  int s_desc_per_block_bits;
  int s_inode_size;
  int s_first_ino;
  //spinlock_t s_next_gen_lock;
  u32_t s_next_generation;
  unsigned long s_dir_count;
  u8_t *s_debts;
  signed long long s_freeblocks_counter;
  signed long long s_freeinodes_counter;
  signed long long s_dirs_counter;
  //struct blockgroup_lock *s_blockgroup_lock;
  /* root of the per fs reservation window tree */
  //spinlock_t s_rsv_window_lock;
  struct rb_root s_rsv_window_root;
  struct ext2_reserve_window_node s_rsv_window_head;
};

/*
 * Structure of a blocks group descriptor
 */
struct ext2_group_desc
{ 
  u32_t  bg_block_bitmap;    /* Blocks bitmap block */
  u32_t  bg_inode_bitmap;    /* Inodes bitmap block */
  u32_t  bg_inode_table;   /* Inodes table block */
  u16_t  bg_free_blocks_count; /* Free blocks count */
  u16_t  bg_free_inodes_count; /* Free inodes count */
  u16_t  bg_used_dirs_count; /* Directories count */
  u16_t  bg_pad;
  u32_t  bg_reserved[3];
};

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
  u16_t  i_mode;   /* File mode */
  u16_t  i_uid;    /* Low 16 bits of Owner Uid */
  u32_t  i_size;   /* Size in bytes */
  u32_t  i_atime;  /* Access time */
  u32_t  i_ctime;  /* Creation time */
  u32_t  i_mtime;  /* Modification time */
  u32_t  i_dtime;  /* Deletion Time */
  u16_t  i_gid;    /* Low 16 bits of Group Id */
  u16_t  i_links_count;  /* Links count */
  u32_t  i_blocks; /* Blocks count */
  u32_t  i_flags;  /* File flags */
  union {
    struct {
      u32_t  l_i_reserved1;
    } linux1;
    struct {
      u32_t  h_i_translator;
    } hurd1; 
    struct {
      u32_t  m_i_reserved1;
    } masix1;
  } osd1;       /* OS dependent 1 */
  u32_t  i_block[EXT2_N_BLOCKS];/* Pointers to blocks */
  u32_t  i_generation; /* File version (for NFS) */
  u32_t  i_file_acl; /* File ACL */
  u32_t  i_dir_acl;  /* Directory ACL */
  u32_t  i_faddr;  /* Fragment address */
  union {
    struct {
      u8_t  l_i_frag; /* Fragment number */
      u8_t  l_i_fsize;  /* Fragment size */
      u16_t i_pad1;
      u16_t  l_i_uid_high; /* these 2 fields    */
      u16_t  l_i_gid_high; /* were reserved2[0] */
      u32_t l_i_reserved2;
    } linux2;
    struct {
      u8_t  h_i_frag; /* Fragment number */
      u8_t  h_i_fsize;  /* Fragment size */
      u16_t  h_i_mode_high;
      u16_t  h_i_uid_high;
      u16_t  h_i_gid_high;
      u32_t  h_i_author;
    } hurd2;
    struct {
      u8_t  m_i_frag; /* Fragment number */
      u8_t  m_i_fsize;  /* Fragment size */
      u16_t m_pad1;
      u32_t m_i_reserved2[2];
    } masix2;
  } osd2;       /* OS dependent 2 */
};
#define i_size_high i_dir_acl

#define i_reserved1 osd1.linux1.l_i_reserved1
#define i_frag    osd2.linux2.l_i_frag
#define i_fsize   osd2.linux2.l_i_fsize
#define i_uid_low i_uid
#define i_gid_low i_gid
#define i_uid_high  osd2.linux2.l_i_uid_high
#define i_gid_high  osd2.linux2.l_i_gid_high
#define i_reserved2 osd2.linux2.l_i_reserved2

/*
 * Define EXT2_RESERVATION to reserve data blocks for expanding files
 */
#define EXT2_DEFAULT_RESERVE_BLOCKS     8
/*max window size: 1024(direct blocks) + 3([t,d]indirect blocks) */
#define EXT2_MAX_RESERVE_BLOCKS         1027
#define EXT2_RESERVE_WINDOW_NOT_ALLOCATED 0

struct ext2_block_alloc_info {
  /* information about reservation window */
  struct ext2_reserve_window_node rsv_window_node;
  /*  
   * was i_next_alloc_block in ext2_inode_info
   * is the logical (file-relative) number of the
   * most-recently-allocated block in this file.
   * We use this for detecting linearly ascending allocation requests.
   */
  u32_t     last_alloc_logical_block;
  /*  
   * Was i_next_alloc_goal in ext2_inode_info
   * is the *physical* companion to i_next_alloc_block.
   * it the the physical block number of the block which was most-recentl
   * allocated to this file.  This give us the goal (target) for the next
   * allocation when we detect linearly ascending requests.
   */
  ext2_fsblk_t    last_alloc_physical_block;
};

/*
 * second extended file system inode data in memory
 */
struct ext2_inode_info {
	/*
	 *Don't be surprised by this capitalized 'I' here.
 	 *Please use 'Ctrl + ]' to find it out if you are using vi.
	 */
	u32_t I_data[15];
  u32_t i_flags;
  u32_t i_faddr;
  u8_t  i_frag_no;
  u8_t  i_frag_size;
  u16_t i_state;
  u32_t i_file_acl;
  u32_t i_dir_acl;
  u32_t i_dtime;

	loff_t i_size;

  /*  
   * i_block_group is the number of the block group which contains
   * this file's inode.  Constant across the lifetime of the inode,
   * it is used for making block allocation decisions - we try to
   * place a file's data blocks near its inode block, and new inodes
   * near to their parent directory's inode.
   */
  u32_t i_block_group;

  u32_t i_dir_start_lookup;
  /* block reservation info */
  struct ext2_block_alloc_info *i_block_alloc_info;
	struct ext2_inode ext2_raw_inode;
#if 0

#ifdef CONFIG_EXT2_FS_XATTR
  /*  
   * Extended attributes can be read independently of the main file
   * data. Taking i_mutex even when reading would cause contention
   * between readers of EAs and writers of regular file data, so
   * instead we synchronize on xattr_sem when reading or changing
   * EAs.
   */
  struct rw_semaphore xattr_sem;
#endif
  rwlock_t i_meta_lock;

  /*  
   * truncate_mutex is for serialising ext2_truncate() against
   * ext2_getblock().  It also protects the internals of the inode's
   * reservation data structures: ext2_reserve_window and
   * ext2_reserve_window_node.
   */
  struct mutex truncate_mutex;
  struct inode  vfs_inode;
  struct list_head i_orphan;  /* unlinked but open inodes */
#endif
};

/*
 * Special inode numbers
 */
#define EXT2_BAD_INO     1  /* Bad blocks inode */
#define EXT2_ROOT_INO    2  /* Root inode */
#define EXT2_BOOT_LOADER_INO   5  /* Boot loader inode */
#define EXT2_UNDEL_DIR_INO   6  /* Undelete directory inode */

#define EXT2_SUPER_MAGIC  0xEF53

/*
 * Revision levels
 */
#define EXT2_GOOD_OLD_REV 0 /* The good old (original) format */
#define EXT2_DYNAMIC_REV  1   /* V2 format w/ dynamic inode sizes */

#define EXT2_CURRENT_REV  EXT2_GOOD_OLD_REV
#define EXT2_MAX_SUPP_REV EXT2_DYNAMIC_REV

#define EXT2_GOOD_OLD_INODE_SIZE 128

/* First non-reserved inode for old ext2 filesystems */
#define EXT2_GOOD_OLD_FIRST_INO 11

#define EXT2_MIN_FRAG_SIZE    1024

/*
 *EXT2_BLOCK_SIZE is the logical block size of the ext2 filesystem.
 *EXT2_BLOCK_GRAN is the logical block size normalized to the size of sectors.
 */
#define EXT2_BLOCK_BITS		12
#define EXT2_BLOCK_SIZE		(1 << EXT2_BLOCK_BITS)
#define EXT2_BLOCK_GRAN		(EXT2_BLOCK_SIZE >> 9)

#define EXT2_ADDR_PER_BLOCK    (EXT2_BLOCK_SIZE / sizeof (unsigned long))

/*
 * Feature set definitions
 */
#define EXT2_HAS_COMPAT_FEATURE(sbi,mask)      \
  ( sbi->s_es->s_feature_compat & mask )
#define EXT2_HAS_RO_COMPAT_FEATURE(sbi,mask)     \
  ( sbi->s_es->s_feature_ro_compat & mask )
#define EXT2_HAS_INCOMPAT_FEATURE(sbi,mask) \
	( sbi->s_es->s_feature_incompat & mask )
#define EXT2_SET_COMPAT_FEATURE(sbi,mask)      \
  sbi->s_es->s_feature_compat |= mask
#define EXT2_SET_RO_COMPAT_FEATURE(sbi,mask)     \
  sbi->s_es->s_feature_ro_compat |= mask
#define EXT2_SET_INCOMPAT_FEATURE(sbi,mask)      \
  sbi->s_es->s_feature_incompat |= mask
#define EXT2_CLEAR_COMPAT_FEATURE(sbi,mask)      \
  sbi->s_es->s_feature_compat &= ~mask
#define EXT2_CLEAR_RO_COMPAT_FEATURE(sbi,mask)     \
  sbi->s_es->s_feature_ro_compat &= ~mask
#define EXT2_CLEAR_INCOMPAT_FEATURE(sbi,mask)      \
  sbi->s_es->s_feature_incompat &= ~mask

#define EXT2_FEATURE_COMPAT_DIR_PREALLOC  0x0001
#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002
#define EXT3_FEATURE_COMPAT_HAS_JOURNAL   0x0004
#define EXT2_FEATURE_COMPAT_EXT_ATTR    0x0008
#define EXT2_FEATURE_COMPAT_RESIZE_INO    0x0010
#define EXT2_FEATURE_COMPAT_DIR_INDEX   0x0020
#define EXT2_FEATURE_COMPAT_ANY     0xffffffff

#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001
#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002
#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR  0x0004
#define EXT2_FEATURE_RO_COMPAT_ANY    0xffffffff
  
#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001
#define EXT2_FEATURE_INCOMPAT_FILETYPE    0x0002
#define EXT3_FEATURE_INCOMPAT_RECOVER   0x0004
#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008
#define EXT2_FEATURE_INCOMPAT_META_BG   0x0010
#define EXT2_FEATURE_INCOMPAT_ANY   0xffffffff

/*
 * Default values for user and/or group using reserved blocks
 */
#define EXT2_DEF_RESUID   0
#define EXT2_DEF_RESGID   0

/*
 * Mount flags
 */
#define EXT2_MOUNT_CHECK    0x000001  /* Do mount-time checks */
#define EXT2_MOUNT_OLDALLOC   0x000002  /* Don't use the new Orlov allocator */
#define EXT2_MOUNT_GRPID    0x000004  /* Create files with directory's group */
#define EXT2_MOUNT_DEBUG    0x000008  /* Some debugging messages */
#define EXT2_MOUNT_ERRORS_CONT    0x000010  /* Continue on errors */
#define EXT2_MOUNT_ERRORS_RO    0x000020  /* Remount fs ro on errors */
#define EXT2_MOUNT_ERRORS_PANIC   0x000040  /* Panic on errors */
#define EXT2_MOUNT_MINIX_DF   0x000080  /* Mimics the Minix statfs */
#define EXT2_MOUNT_NOBH     0x000100  /* No buffer_heads */
#define EXT2_MOUNT_NO_UID32   0x000200  /* Disable 32-bit UIDs */
#define EXT2_MOUNT_XATTR_USER   0x004000  /* Extended user attributes */
#define EXT2_MOUNT_POSIX_ACL    0x008000  /* POSIX Access Control Lists */
#define EXT2_MOUNT_XIP      0x010000  /* Execute in place */
#define EXT2_MOUNT_USRQUOTA   0x020000  /* user quota */
#define EXT2_MOUNT_GRPQUOTA   0x040000  /* group quota */
#define EXT2_MOUNT_RESERVATION    0x080000  /* Preallocation */
  
/*  
 * Default mount options
 */
#define EXT2_DEFM_DEBUG   0x0001
#define EXT2_DEFM_BSDGROUPS 0x0002
#define EXT2_DEFM_XATTR_USER  0x0004
#define EXT2_DEFM_ACL   0x0008
#define EXT2_DEFM_UID16   0x0010

#define clear_opt(o, opt)   o &= ~EXT2_MOUNT_##opt
#define set_opt(o, opt)     o |= EXT2_MOUNT_##opt
#define test_opt(sb, opt)   (EXT2_SB(sb)->s_mount_opt & \
           EXT2_MOUNT_##opt)

/* data type for block offset of block group */
typedef int ext2_grpblk_t;

/*
 * Ok, these declarations are also in <linux/kernel.h> but none of the
 * ext2 source programs needs to include it so they are duplicated here.
 */

static inline struct ext2_inode_info *EXT2_I(struct inode *inode)
{
	return inode->i_data;
} 

static inline struct ext2_sb_info *EXT2_SB(struct super_block *sb)
{
  return sb->s_data;
}

#define EXT2_BLOCKS_PER_GROUP(s) (EXT2_SB(s)->s_blocks_per_group)
#define EXT2_DESC_PER_BLOCK(s)   (EXT2_SB(s)->s_desc_per_block)
#define EXT2_INODES_PER_GROUP(s) (EXT2_SB(s)->s_inodes_per_group)
#define EXT2_DESC_PER_BLOCK_BITS(s)  (EXT2_SB(s)->s_desc_per_block_bits)

#define EXT2_ADDR_PER_BLOCK_BITS(s) (EXT2_SB(s)->s_addr_per_block_bits)
#define EXT2_INODE_SIZE(s)    (EXT2_SB(s)->s_inode_size)
#define EXT2_FIRST_INO(s)   (EXT2_SB(s)->s_first_ino)

#define MS_POSIXACL (1<<16) /* VFS does not apply the umask */

#define le16_to_cpu(x) (x)
#define le32_to_cpu(x) (x)
#define cpu_to_le16(x) (x)
#define cpu_to_le32(x) (x)

#define ext2_find_next_zero_bit(addr, size, off) \
  find_next_zero_bit((unsigned long *)(addr), (size), (off))
  //generic_find_next_zero_le_bit((unsigned long *)(addr), (size), (off))

#define ext2_test_bit(nr,addr)  \
  test_bit((nr),(unsigned long *)(addr))

/*
 * Inode flags (GETFLAGS/SETFLAGS)
 */
#define EXT2_SECRM_FL     FS_SECRM_FL /* Secure deletion */
#define EXT2_UNRM_FL      FS_UNRM_FL  /* Undelete */
#define EXT2_COMPR_FL     FS_COMPR_FL /* Compress file */
#define EXT2_SYNC_FL      FS_SYNC_FL  /* Synchronous updates */
#define EXT2_IMMUTABLE_FL   FS_IMMUTABLE_FL /* Immutable file */
#define EXT2_APPEND_FL      FS_APPEND_FL  /* writes to file may only append */
#define EXT2_NODUMP_FL      FS_NODUMP_FL  /* do not dump file */
#define EXT2_NOATIME_FL     FS_NOATIME_FL /* do not update atime */
/* Reserved for compression usage... */
#define EXT2_DIRTY_FL     FS_DIRTY_FL
#define EXT2_COMPRBLK_FL    FS_COMPRBLK_FL  /* One or more compressed clusters */
#define EXT2_NOCOMP_FL      FS_NOCOMP_FL  /* Don't compress */
#define EXT2_ECOMPR_FL      FS_ECOMPR_FL  /* Compression error */
/* End compression flags --- maybe not all used */
#define EXT2_BTREE_FL     FS_BTREE_FL /* btree format dir */
#define EXT2_INDEX_FL     FS_INDEX_FL /* hash-indexed directory */
#define EXT2_IMAGIC_FL      FS_IMAGIC_FL  /* AFS directory */
#define EXT2_JOURNAL_DATA_FL    FS_JOURNAL_DATA_FL /* Reserved for ext3 */
#define EXT2_NOTAIL_FL      FS_NOTAIL_FL  /* file tail should not be merged */
#define EXT2_DIRSYNC_FL     FS_DIRSYNC_FL /* dirsync behaviour (directories only) */
#define EXT2_TOPDIR_FL      FS_TOPDIR_FL  /* Top of directory hierarchies*/
#define EXT2_RESERVED_FL    FS_RESERVED_FL  /* reserved for ext2 lib */
  
#define EXT2_FL_USER_VISIBLE    FS_FL_USER_VISIBLE  /* User visible flags */
#define EXT2_FL_USER_MODIFIABLE   FS_FL_USER_MODIFIABLE /* User modifiable flags */
  
/* Flags that should be inherited by new inodes from their parent. */
#define EXT2_FL_INHERITED (EXT2_SECRM_FL | EXT2_UNRM_FL | EXT2_COMPR_FL |\
         EXT2_SYNC_FL | EXT2_IMMUTABLE_FL | EXT2_APPEND_FL |\
         EXT2_NODUMP_FL | EXT2_NOATIME_FL | EXT2_COMPRBLK_FL|\
         EXT2_NOCOMP_FL | EXT2_JOURNAL_DATA_FL |\
         EXT2_NOTAIL_FL | EXT2_DIRSYNC_FL)
  
/* Flags that are appropriate for regular files (all but dir-specific ones). */
#define EXT2_REG_FLMASK (~(EXT2_DIRSYNC_FL | EXT2_TOPDIR_FL))
  
/* Flags that are appropriate for non-directories/regular files. */
#define EXT2_OTHER_FLMASK (EXT2_NODUMP_FL | EXT2_NOATIME_FL)

static inline u32_t ext2_mask_flags(mode_t mode, u32_t flags)
{ 
  if (S_ISDIR(mode))
    return flags;
  else if (S_ISREG(mode))
    return flags & EXT2_REG_FLMASK;
  else
    return flags & EXT2_OTHER_FLMASK;
}

/*
 * Inode dynamic state flags
 */
#define EXT2_STATE_NEW      0x00000001 /* inode is newly created */

static inline unsigned long 
ext2_group_first_block_no(struct ext2_sb_info *sbi, unsigned long group_no)
{
  return group_no * (unsigned long)sbi->s_blocks_per_group +
    sbi->s_es->s_first_data_block;
}

/*
 * The new version of the directory entry.  Since EXT2 structures are
 * stored in intel byte order, and the name_len field could never be
 * bigger than 255 chars, it's safe to reclaim the extra byte for the
 * file_type field.
 */
#define EXT2_NAME_LEN 255
#define EXT2_LINK_LEN 60

#define ext2_dirent ext2_dir_entry_2
struct ext2_dir_entry_2 {
  u32_t  inode;      /* Inode number */
  u16_t  rec_len;    /* Directory entry length */
  u8_t  name_len;   /* Name length */
  u8_t  file_type;
  char  name[EXT2_NAME_LEN];  /* File name */
};

/*
 * Ext2 directory file types.  Only the low 3 bits are used.  The
 * other bits are reserved for now.
 */
enum {
  EXT2_FT_UNKNOWN   = 0,
  EXT2_FT_REG_FILE  = 1,
  EXT2_FT_DIR   = 2,
  EXT2_FT_CHRDEV    = 3,
  EXT2_FT_BLKDEV    = 4,
  EXT2_FT_FIFO    = 5,
  EXT2_FT_SOCK    = 6,
  EXT2_FT_SYMLINK   = 7,
  EXT2_FT_MAX
};

/*
 * EXT2_DIR_PAD defines the directory entries boundaries
 *
 * NOTE: It must be a multiple of 4
 */
#define EXT2_DIR_PAD      4
#define EXT2_DIR_ROUND      (EXT2_DIR_PAD - 1)
#define EXT2_DIR_REC_LEN(name_len)  (((name_len) + 8 + EXT2_DIR_ROUND) & \
           ~EXT2_DIR_ROUND)
#define EXT2_MAX_REC_LEN    ((1<<16)-1)

#define EXT2_DIRENT_CHUNK_SIZE	512

static inline unsigned ext2_rec_len_from_disk(u16_t dlen)
{
  unsigned len = le16_to_cpu(dlen);

  if (len == EXT2_MAX_REC_LEN){
		printk("ext2_rec_len_from_disk() observes extremely long entry.\n");
    return 1 << 16; 
	}
  return len;
}

static inline unsigned short ext2_rec_len_to_disk(unsigned len)
{
  if (len == (1 << 16)){
		printk("ext2_rec_len_to_disk observes extremely long entry.\n");
    return cpu_to_le16(EXT2_MAX_REC_LEN);
  }else if(len > (1 << 16)){
		printk("Invalid length. %s %s\n",__func__,__FILE__);
		BUG();
	}
  return cpu_to_le16(len);
}

/*
 * p is at least 6 bytes before the end of page
 */
static inline struct ext2_dirent *ext2_next_entry(struct ext2_dirent *p)
{
  return (struct ext2_dirent *)((char *)p +
      ext2_rec_len_from_disk(p->rec_len));
}

#endif
